The Oracle [crypto]

The Oracle

The oracle knows all, and it's kinda chatty. Is it telling you something?

nc chal.tuctf.com 30103

Recon

Upon connecting to the service, we're given a ciphertext and a menu to check the padding of the decrypted plaintext or enter a password. The assumption is that the password is the decrypted ciphertext we've been given.

As an initial guess, given the "check padding" function and that the ciphertexts are always a multiple of 128 bits, it seems like a CBC padding oracle with AES as the block cipher under the hood.

Welcome! The Oracle will see you now!


Your ciphertext is:

E3bVPSGYGadezlvcanGklpWiu0mnZWo9shKob1AiIOZLqO3Q96z96wWPfl0YBBA2

MENU:

1) Check padding
2) Enter password

1

Give me your input: E3bVPSGYGadezlvcanGklpWiu0mnZWo9shKob1AiIOZLqO3Q96z96wWPfl0YBBA2

Padding Valid

MENU:

1) Check padding
2) Enter password

22
Not a valid option

MENU:

1) Check padding
2) Enter password

2

What is the password? TEST

That's incorrect

MENU:

1) Check padding
2) Enter password

So the goal is to decrypt the ciphertext, then send the plaintext as the password.

Code

We've used the solid python-paddingoracle framework from https://github.com/mwielgoszewski/python-paddingoracle to quickly build an exploit to get the password:

from pwn import *
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
import time

class PadBuster(PaddingOracle):
    def __init__(self, c, **kwargs):
        super(PadBuster, self).__init__(**kwargs)
        self.c = c

    def oracle(self, data, **kwargs):
        ct = b64encode(data)

        while True:
            try:
                c.send("1\n")
                c.send(ct + "\n")
                c.readuntil("input:")
                c.readuntil("Padding ")
                response = c.readuntil("\n").strip()
                logging.debug("GOT RESPONSE: %s" % response)
                c.readuntil("password\n")
                break
            except Exception as e:
                logging.exception(e)
                continue

        self.history.append(response)

        if "Valid" in response:
            logging.info("Got valid padding for %r" % ct)
            return

        logging.debug("Invalid padding for %r" % ct)

        raise BadPaddingException

def get_ct():
    c = remote("chal.tuctf.com", 30103)
    r = c.readuntil("Your ciphertext is:\n\n")
    print("< " + r)
    ct = c.readuntil("\n").strip()
    print("< " + c.readuntil("password\n\n"))
    return ct, c


if __name__ == '__main__':
    import logging
    import sys

    logging.basicConfig(level=logging.DEBUG)

    ct, c = get_ct()

    print("GOT CT: " + ct)

    ct = b64decode(ct)
    padbuster = PadBuster(c)

    plain = padbuster.decrypt(ct, block_size=16)

    print('Decrypted ciphertext: %s => %r' % (ct, plain))
Decrypted ciphertext: yx���R\x03\x1eC\x985����5R\x94x�h�\x93&t<\x1c���\x8b�T���3   ��\xb7>�o�Ȯ
=> bytearray(b'SUPERSECRETPASSWORDKEEPAWAY!\x04\x04\x04\x04')
[*] Closed connection to chal.tuctf.com port 30103
INFO:pwnlib.tubes.remote.remote.140695383547088:Closed connection to chal.tuctf.com port 30103

Solution

The ciphertext decrypts to the password SUPERSECRETPASSWORDKEEPAWAY!, which gives us the flag.

Welcome! The Oracle will see you now!


Your ciphertext is:

NKQdIU2V1+2NtRueQIj5HWLfn3+H1+dYHXpg5mtX5QIlwg6j/wmjyyTxc/021e1t

MENU:

1) Check padding
2) Enter password

2

What is the password? SUPERSECRETPASSWORDKEEPAWAY!

That's it!

Congratulations!

Here's your flag:


TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}

Flag

TUCTF{D0nt_l3t_y0ur_s3rv3r_g1v3_f33db4ck}